package com.young.common.client.log.core;

import com.young.common.core.concurrent.YoungThreadPoolExecutor;
import com.young.common.core.concurrent.rejected.YoungDiscardPolicy;
import com.young.interfaces.log.model.LogDTO;
import org.aspectj.lang.JoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 日志服务控制
 * Created by rookie on 2017/11/10.
 */
@Configuration
@ConditionalOnBean({LogHandlerFactory.class})
@Scope("singleton")
public class LogControl {

    /**
     * 日志队列,用于存放日志信息
     * 基于链表的阻塞队列,使用2把锁来分别管理读和写,并发性能更好.
     */
    private static LinkedBlockingQueue<LogDTO> logQueue = new LinkedBlockingQueue<LogDTO>();

    /**
     * 线程池,用于处理LogCatchTask
     */
    private static ExecutorService threadPool = YoungThreadPoolExecutor.builder("LogCatchTaskPool").workQueue(new LinkedBlockingDeque<>(1000)).rejectedHandler(new YoungDiscardPolicy()).build();

    /**
     * 消费线程池,用于处理日志消费任务
     */
    private static ExecutorService threadPool2 = YoungThreadPoolExecutor.builder("LogConsumeTaskPool").workQueue(new LinkedBlockingDeque<>(1000)).rejectedHandler(new YoungDiscardPolicy()).build();

    /**
     * 已捕获日志总数,原子操作int,避免高并发下的线程安全问题
     */
    private static AtomicInteger catchLogNum = new AtomicInteger();

    private static final Logger logger = LoggerFactory.getLogger(LogControl.class);

    static {
        //每半分钟统计一下线程池运行清空
        /*Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                if (logger.isInfoEnabled()) {
                    ThreadPoolExecutor pool1 = (ThreadPoolExecutor) threadPool;
                    ThreadPoolExecutor pool2 = (ThreadPoolExecutor) threadPool2;
                    logger.info("[日志生产线程池运行情况] -线程池大小:{} -正在执行任务的线程数:{} -总任务数:{} -已完成任务数:{} -堆积任务数:{} -核心线程数:{} -最大线程数:{} -峰值线程数:{}",
                            pool1.getPoolSize(), pool1.getActiveCount(), pool1.getTaskCount(), pool1.getCompletedTaskCount(), pool1.getQueue().size(), pool1.getCorePoolSize(), pool1.getMaximumPoolSize(), pool1.getLargestPoolSize());
                    logger.info("[日志消费线程池运行情况] -线程池大小:{} -正在执行任务的线程数:{} -总任务数:{} -已完成任务数:{} -堆积任务数:{} -核心线程数:{} -最大线程数:{} -峰值线程数:{}",
                            pool2.getPoolSize(), pool2.getActiveCount(), pool2.getTaskCount(), pool2.getCompletedTaskCount(), pool2.getQueue().size(), pool2.getCorePoolSize(), pool2.getMaximumPoolSize(), pool2.getLargestPoolSize());
                }
            }
        }, 10, 60, TimeUnit.SECONDS);*/
    }

    /**
     * 抓捕日志(异步)
     * @param joinPoint 切入点对象
     * @param logDTO 日志对象
     */
    public static void catchLog(JoinPoint joinPoint, LogDTO logDTO){
        threadPool.execute(new LogCatchTask(joinPoint, logDTO));
    }

    /**
     * 添加日志到队列中,供日志捕获器LogCatchTask向日志控制中心添加日志对象
     * @param logDTO
     */
    public static void put(LogDTO logDTO){
        try {
            logQueue.put(logDTO);
            catchLogNum.incrementAndGet();//日志数自增
        } catch (InterruptedException e) {
            logger.error("[日志控制中心] 日志存入队列异常,logDTO={},异常:", logDTO, e);
        }
    }

    //spring创建该bean后执行该方法,开始日志消费
    @PostConstruct
    public void consumeStart(){
        logger.info("[日志控制中心] 日志消费线程启动!");
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (!Thread.interrupted()){
                    try {
                        LogDTO logDTO = logQueue.take();
                        threadPool2.execute(new LogConsumeTask(logDTO));
                    } catch (InterruptedException e) {
                        logger.error("[日志控制中心] 从队列获取日志异常:", e);
                    }
                }
            }
        }).start();
    }

    @PreDestroy
    public void destroy(){
        logger.info("[日志控制中心] 销毁处理开始...");
        if(logQueue.size() > 0){
            logger.error("[日志控制中心] 当前日志队列长度为{},销毁可能会导致部分日志丢失!", logQueue.size());
        }else{
            logger.info("[日志控制中心] 日志队列为空,可以进行销毁!");
        }
        logger.info("[日志控制中心] 销毁处理结束!");
    }
}
